/*
 * NyuStrPool.cpp
 *
 *  Created on: 2010/05/22
 *      Author: mitsu
 */

#include <vector>
#include <new>
#include <cstdlib>
#include <cstring>
#include "NyuStrPool.hpp"
#include "NyuString.hpp"

namespace {

std::vector<NyuStaticString*> static_strings_;

}

NyuStrPool::NyuStrPool() : ents_(new Entry*[127]), nents_(127){
    ::memset(ents_, 0, sizeof(Entry*) * nents_);

    for (std::vector<NyuStaticString*>::iterator it = static_strings_.begin(),
            end = static_strings_.end(); it != end; ++it) {
        add(*it);
    }
}

NyuStrPool::~NyuStrPool() {
    for (size_t i = 0, j = nents_; i < j; i++) {
        delete ents_[i];
    }
    delete [] ents_;
}

void NyuStrPool::initialize() {
    instance_ = new NyuStrPool();
}

void NyuStrPool::finalize() {
    delete instance_;
}

NyuString* NyuStrPool::get(char* str, int) {
    unsigned int h = NyuString::calcHash(str);
    unsigned int i = h % nents_;
    Entry* ent = ents_[i];
    int depth = 0;

    while (ent && ::strcmp(ent->value->strval(), str) != 0) {
        ent = ent->next;
        ++depth;
    }

    // 見つかった場合は渡されたstrを削除して保持しているオブジェクトを返す
    if (ent) {
        ::free(str);
        return ent->value;
    }

    // 見つからなかったので作成（GC管理外の領域に。）
    void* p = ::operator new(sizeof(NyuImmutableString));
    ent = new Entry(new(p) NyuImmutableString(str, h), ents_[i]);
    ents_[i] = ent;

    // depthが閾値を越えていたらrehash
    if (depth > 1)
        rehash();

    return ent->value;
}

void NyuStrPool::addStatic(NyuStaticString* str) {
    static_strings_.push_back(str);
}

void NyuStrPool::rehash() {
    size_t new_size, i, j;
    Entry **new_ents, *ent, **ents;

    new_size = nents_ * 2 + 1;

    new_ents = new Entry*[new_size];

    ::memset(new_ents, 0, sizeof(Entry*) * new_size);

    ents = ents_;
    for (i = 0, j = nents_; i < j; i++) {
        for (ent = ents[i]; ent;) {
            Entry* tmp = ent;
            unsigned int h = tmp->value->hash() % new_size;
            ent = ent->next;
            tmp->next = new_ents[h];
            new_ents[h] = tmp;
        }
    }

    delete [] ents;

    nents_ = new_size;
    ents_ = new_ents;
}

void NyuStrPool::add(NyuString* str) {
    unsigned int h = str->hash();
    unsigned int i = h % nents_;
    ents_[i] = new Entry(str, ents_[i]);
}

NyuStrPool::Entry::~Entry() {
    value->dispose();
    delete next;
}

NyuStrPool* NyuStrPool::instance_ = 0;
